﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Data.SqlClient;
using System.Data;
using TSL_Tools.SqlTools;
using ReportingEngine;
using System.IO;
using System.Text.RegularExpressions;
using System.Xml;
using System.Windows.Forms.DataVisualization.Charting;
using System.Windows.Xps.Packaging;
using System.Windows.Xps;
using System.IO.Packaging;

namespace VSLT_Reporting
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        private DataTable _chartObjectsTable;
        private string defaultReportName = "";

        private void GenerateTheReport()
        {

            System.Windows.Input.Cursor _cursor = this.Cursor;
            this.Cursor = Cursors.Wait;
            GetChartObjectColorsFromXmlFile();

            #region -- Core Timing Tables ----------------------------------
            rtbResults.Document.Blocks.Add(ReportGenerator.TSL_ReportGenerator.AddSeparatorLineToReport().Blocks.FirstBlock);
            AddSectionTitleToReport("Core Timing Tables and Charts");
            AddCoreTimingTablesAndChartsToReport();
            rtbResults.Document.Blocks.Add(ReportGenerator.TSL_ReportGenerator.AddSeparatorLineToReport().Blocks.FirstBlock);
            #endregion

            #region -- Errors Table ----------------------------------------
            AddSectionTitleToReport("Error Summary");
            AddErrorSummaryToReport();
            rtbResults.Document.Blocks.Add(ReportGenerator.TSL_ReportGenerator.AddSeparatorLineToReport().Blocks.FirstBlock);
            #endregion

            #region -- Timings Grouped By Name -----------------------------
            if (Properties.Settings.Default.Include_GROUPEDBYNAME_TimingsTablesInReport)
            {
                AddTransactionTimingTable_GroupedByName_ToReport();
                AddPageTimingTable_GroupedByName_ToReport();
            }
            #endregion

            #region -- Interval Timing Tables ------------------------------
            if (Properties.Settings.Default.AddIntervalTimingsToReportWhenAvailable)
            {
                AddTestIntervalTablesToReport();
                AddTransactionIntervalTablesToReport();
                AddPageIntervalTablesToReport();
                AddPerfmonIntervalTablesToReport("", "Processor", "% Processor Time", "_Total", "Processor Values for 'constant load' Intervals");
            }
            #endregion

            #region -- Other Charts ----------------------------------------
            AddSectionTitleToReport("Other Charts");
            GetChartObjectsFromXmlFile();
            AddChartObjectsToSeriesSets();            
            BuildReportCharts();
            rtbResults.Document.Blocks.Add(ReportGenerator.TSL_ReportGenerator.AddSeparatorLineToReport().Blocks.FirstBlock);
            #endregion

            #region -- Scenarios -------------------------------------------
            if (Properties.Settings.Default.includeScenariosInReport)
            {
                AddSectionTitleToReport("Loadtest Run Settings and Scenarios");
                GetLoadTestInfoForHighlightedRun(currentlyHighlightedRunId, true);
                rtbResults.Document.Blocks.Add(ReportGenerator.TSL_ReportGenerator.AddSeparatorLineToReport().Blocks.FirstBlock);
            }
            #endregion

            #region -- Debug Timing Info -----------------------------------
            if (Properties.Settings.Default.includeDebugTimingInfoInReport)
            {
                AddSectionTitleToReport("Report Generation Timing Info");
                AddDebugTimingInfoToReport();
                rtbResults.Document.Blocks.Add(ReportGenerator.TSL_ReportGenerator.AddSeparatorLineToReport().Blocks.FirstBlock);
            }
            #endregion

            this.Cursor = _cursor;
            defaultReportName = String.Format(Msg__FILENAMEFORREPORT, re.listOfRuns[currentlyHighlightedRunId].LoadTestName, currentlyHighlightedRunId);
            btnSaveReport.IsEnabled = true;
            btnGenerateReport.IsEnabled = false;
            Clipboard.SetText(debugTimingInfo.ToString());

        }

        private void PrintTheReport()
        {
                PrintDialog pd = new PrintDialog();
                if ((pd.ShowDialog() == true))
                {
                    pd.PrintDocument((((IDocumentPaginatorSource)rtbResults.Document).DocumentPaginator), "printing as paginator");
                }
        }

        private void SaveTheReport(string fileName)
        {

            if (File.Exists(fileName))
            {
                if (MessageBox.Show(Msg_FILEEXISTS, Msg_WARNING, MessageBoxButton.YesNo) == MessageBoxResult.No)
                    return;
            }

            string extensionType = fileName.Substring(fileName.LastIndexOf("."));
            if (extensionType == ".xps")
            {
                SaveToXps(fileName);
            }
            else
            {
                using (FileStream fs = File.OpenWrite(fileName))
                {
                    TextRange text = new TextRange(rtbResults.Document.ContentStart, rtbResults.Document.ContentEnd);
                    if (extensionType == ".rtf")
                        text.Save(fs, System.Windows.DataFormats.Rtf);
                    else if (extensionType == ".xaml")
                        text.Save(fs, System.Windows.DataFormats.Xaml);
                    else
                        MessageBox.Show(Msg_FILEFORMATERROR);
                }
            }
            tsslMessage.Content = String.Format(Msg_FILESAVED, fileName);
        }

        private void SaveToXps(string fileName)
        {
            FlowDocument fDoc = new FlowDocument();
            fDoc = rtbResults.Document;
            
            XpsDocument doc = new XpsDocument(fileName, FileAccess.Write);
            XpsDocumentWriter writer = XpsDocument.CreateXpsDocumentWriter(doc);
            DocumentPaginator paginator = ((IDocumentPaginatorSource)fDoc).DocumentPaginator;
            paginator = new DocumentPaginatorWrapper(paginator, new Size(96 * 8.5, 96 * 11), new Size(48, 48));
            writer.Write(paginator);
            doc.Close();
        }

        private void SaveTheReport()
        {
            // Configure save file dialog box
            Microsoft.Win32.SaveFileDialog dlg = new Microsoft.Win32.SaveFileDialog();
            dlg.InitialDirectory = Properties.Settings.Default.FolderToStoreResults;
            dlg.FileName = defaultReportName + ".rtf";
            //dlg.DefaultExt = ".rtf";
            dlg.Filter = "RTF Documents|*.rtf|All Files|*.*";

            // Show save file dialog box
            Nullable<bool> result = dlg.ShowDialog();

            // Process save file dialog box results 
            if (result == true)
            {
                // Save document 
                SaveTheReport(dlg.FileName);
            }
        }

        private void AddCoreTimingTablesAndChartsToReport()
        {
            XmlDocument xDoc = new XmlDocument();
            xDoc.Load(Properties.Settings.Default.ChartObjectFileName);

            #region -- Variable Names -------------------------------------------------------------
            foreach (XmlNode xNode in xDoc.GetElementsByTagName("KeyMetricsCounters"))
            {
                foreach (XmlNode xChild in xNode.ChildNodes)
                {
                    if (xChild.Attributes != null)
                    {
                        if (xChild.Attributes["name"].Value == "Average Test Time" 
                            && xChild.Attributes["enabled"].Value.ToLower() == "true")
                        {
                            AddCoreTestCaseTableAndChartToReport();
                        }
                        else if (xChild.Attributes["name"].Value == "Average Transaction Time"
                            && xChild.Attributes["enabled"].Value.ToLower() == "true")
                        {
                            AddCoreTransactionTableAndChartToReport();
                        }
                        else if (xChild.Attributes["name"].Value == "Average Page Time"
                            && xChild.Attributes["enabled"].Value.ToLower() == "true")
                        {
                            AddCorePageTableAndChartToReport();
                        }
                    }
                }
            }
            #endregion
        }

        private void AddErrorSummaryToReport()
        {
            // Get the error data
            string sqlCmd = String.Format("EXEC VSLT_prc_ErrorSummary @RunId = {0},	@NumberOfRows = {1}",
                currentlyHighlightedRunId, Properties.Settings.Default.numTopSlowest);
            DataTable _table;
            TalkToSql tts = new TalkToSql(re.sqlConn, Properties.Settings.Default.ConnectionTimeout, Properties.Settings.Default.CommandTimeout);
            TimeSpan ts = tts.ExecuteReaderTable(sqlCmd, out _table);
            mostRecentSqlQuery = sqlCmd;


            // Add the data to a table
            if (_table.Rows.Count > 0)
            {
                Typeface font = new Typeface("Arial");
                FlowDocument flowDocTemp = ReportGenerator.TSL_ReportGenerator.CreateXamlTableFromDataTable("Error Summary", _table, new List<int>(new int[] { 32, 150, 250, 150, 150 }), font, 11);
                rtbResults.Document.Blocks.Add(flowDocTemp.Blocks.FirstBlock);
                rtbResults.Document.Blocks.Add(ReportGenerator.TSL_ReportGenerator.AddSeparatorLineToReport().Blocks.FirstBlock);
                rtbResults.UpdateLayout();
            }
            else
            {
                rtbResults.Document.Blocks.Add(ReportGenerator.TSL_ReportGenerator.AddCommentLineToReport("No Errors found in the run", Colors.Blue));
                rtbResults.Document.Blocks.Add(ReportGenerator.TSL_ReportGenerator.AddSeparatorLineToReport().Blocks.FirstBlock);
                rtbResults.UpdateLayout();
            }
        }

        private void AddTransactionTimingTable_GroupedByName_ToReport()
        {
            if (re.listOfRuns[currentlyHighlightedRunId].resultTypes.HasFlag(ResultTypesStoredInLoadTest.ContainsTransactions))
            {
                string sqlCmd = String.Format(TSL_ReportingEngine.Sql_TRANSACTIONRESULTS_GROUPEDBYNAME,
                    currentlyHighlightedRunId,
                    "NULL",
                    "NULL",
                    tbTableFilterString.Text,
                    Properties.Settings.Default.numTopSlowest);
                DataTable _table;
                TalkToSql tts = new TalkToSql(re.sqlConn, Properties.Settings.Default.ConnectionTimeout, Properties.Settings.Default.CommandTimeout);
                TimeSpan ts = tts.ExecuteReaderTable(sqlCmd, out _table);
                mostRecentSqlQuery = sqlCmd;

                if (_table.Rows.Count > 0)
                {
                    AddSectionTitleToReport("Transaction Summaries Grouped By Name");
                    Typeface font = new Typeface("Arial");
                    FlowDocument flowDocTemp = ReportGenerator.TSL_ReportGenerator.CreateXamlTableFromDataTable("", _table, font, 10, 32);
                    rtbResults.Document.Blocks.Add(flowDocTemp.Blocks.FirstBlock);
                    rtbResults.Document.Blocks.Add(ReportGenerator.TSL_ReportGenerator.AddSeparatorLineToReport().Blocks.FirstBlock);
                    rtbResults.UpdateLayout();
                }
            }
        }

        private void AddPageTimingTable_GroupedByName_ToReport()
        {

            if (re.listOfRuns[currentlyHighlightedRunId].resultTypes.HasFlag(ResultTypesStoredInLoadTest.ContainsPages))
            {
                string sqlCmd = String.Format(TSL_ReportingEngine.Sql_PAGERESULTS_GROUPEDBYNAME,
                    currentlyHighlightedRunId,
                    "NULL",
                    "NULL",
                    tbTableFilterString.Text,
                    Properties.Settings.Default.numTopSlowest);
                DataTable _table;
                TalkToSql tts = new TalkToSql(re.sqlConn, Properties.Settings.Default.ConnectionTimeout, Properties.Settings.Default.CommandTimeout);
                TimeSpan ts = tts.ExecuteReaderTable(sqlCmd, out _table);
                mostRecentSqlQuery = sqlCmd;

                if (_table.Rows.Count > 0)
                {
                    AddSectionTitleToReport("Page Summaries Grouped By Name");

                    Typeface font = new Typeface("Arial");
                    FlowDocument flowDocTemp = ReportGenerator.TSL_ReportGenerator.CreateXamlTableFromDataTable("", _table, font, 10, 32);

                    rtbResults.Document.Blocks.Add(flowDocTemp.Blocks.FirstBlock);
                    rtbResults.Document.Blocks.Add(ReportGenerator.TSL_ReportGenerator.AddSeparatorLineToReport().Blocks.FirstBlock);
                    rtbResults.UpdateLayout();
                }
            }
        }
        
        #region -- Support methods for the summary charts ----------------------------------------------------------------------------
        private string BuildRunDataWithChartTableForReport(ref DataTable _table, string tableType)
        {
            DateTime dt = DateTime.Now;

            StringBuilder finalText = new StringBuilder();
            string darkRowText;
            string lightRowText;

            tsslMessage.Content = String.Format("Building {0} Results Table", tableType);

            using (StreamReader sr = new StreamReader("Templates\\ChartTablePassHeader.xaml"))
            {
                finalText.Append(sr.ReadToEnd());
                finalText.Replace("{{TableType}}", tableType);
            }

            using (StreamReader sr = new StreamReader("Templates\\ChartTablePassDarkRow.xaml"))
            {
                darkRowText = sr.ReadToEnd();
            }

            using (StreamReader sr = new StreamReader("Templates\\ChartTablePassLightRow.xaml"))
            {
                lightRowText = sr.ReadToEnd();
            }

            currentPlotColor = 0;
            for (int x = 0; x < _table.Rows.Count; x++)
            {
                StringBuilder sb = new StringBuilder();
                sb.Append((x % 2 == 0) ? darkRowText : lightRowText);
                sb.Replace("{{CellColor}}", System.Drawing.Color.FromArgb(plotColors[currentPlotColor % plotColors.Count].ToArgb()).Name.ToString());
                currentPlotColor++;
                for (int y = 0; y < _table.Columns.Count; y++)
                {
                    // NOTE: the first column (level = 0) is not a data column in the chart so we start at column 1
                    sb.Replace(("{{" + (y + 1).ToString() + "}}"), _table.Rows[x][y].ToString());
                }
                finalText.Append(sb.ToString());
            }

            //NOTE: Adding this to handle any items that may not have gotten replaced

            finalText.Append("</TableRowGroup></Table>");


            //AddDebugTimingInfo((TimeSpan)(DateTime.Now - dt), "BuildRunDataWithChartTableForReport");
            return CleanUpExtraContextsInReportString(finalText.ToString());
        }

        private string BuildRunDataWithoutChartTableForReport(ref DataTable _table, string tableType)
        {
            DateTime dt = DateTime.Now;

            StringBuilder finalText = new StringBuilder();
            string darkRowText;
            string lightRowText;

            tsslMessage.Content = String.Format("Building {0} Results Table", tableType);

            using (StreamReader sr = new StreamReader("Templates\\TablePassHeader.xaml"))
            {
                finalText.Append(sr.ReadToEnd());
                finalText.Replace("{{TableType}}", tableType);
            }

            using (StreamReader sr = new StreamReader("Templates\\TablePassDarkRow.xaml"))
            {
                darkRowText = sr.ReadToEnd();
            }

            using (StreamReader sr = new StreamReader("Templates\\TablePassLightRow.xaml"))
            {
                lightRowText = sr.ReadToEnd();
            }

            currentPlotColor = 0;
            for (int x = 0; x < _table.Rows.Count; x++)
            {
                StringBuilder sb = new StringBuilder();
                sb.Append((x % 2 == 0) ? darkRowText : lightRowText);
                for (int y = 0; y < _table.Columns.Count; y++)
                {
                    sb.Replace(("{{" + y.ToString() + "}}"), _table.Rows[x][y].ToString());
                }
                finalText.Append(sb.ToString());
            }

            //NOTE: Adding this to handle any items that may not have gotten replaced

            finalText.Append("</TableRowGroup></Table>");


            //AddDebugTimingInfo((TimeSpan)(DateTime.Now - dt), "BuildRunDataWithoutChartTableForReport");
            return CleanUpExtraContextsInReportString(finalText.ToString());
        }

        private string CleanUpExtraContextsInReportString(string str)
        {
            DateTime dt = DateTime.Now;

            string pattern = @"\{\{.+?}}";

            string replacement = "[[RegexReplaced]]";
            Regex rgx = new Regex(pattern);

            //AddDebugTimingInfo((TimeSpan)(DateTime.Now - dt), "CleanUpExtraContextsInReportString");
            return rgx.Replace(str, replacement);
        }
        #endregion

        #region -- Chart Data Series Building Routines ------------------------------------------------------------------------------
        private TimeSpan BuildReportCharts()
        {
            DateTime dt = DateTime.Now;

            listOfSeriesToGroup.Clear();
            int x = 0;
            string currentChartSetName = chartObjects[x].name;
            string chartTitle = currentChartSetName;

            do
            {
                if (currentChartSetName == chartObjects[x].name)
                {
                    listOfSeriesToGroup.Add(x);
                }
                else //Found the first item of the next grouping. Process the previous group and reset to build the next group
                {
                    chartTitle = currentChartSetName;
                    AddDebugTimingInfo(BuildNewChart(listOfSeriesToGroup, chartTitle, true), " -- BuildNewChart " + chartTitle);
                    //BuildNewChart(listOfSeriesToGroup, chartTitle, true);
                    currentChartSetName = chartObjects[x].name;
                    listOfSeriesToGroup.Clear();
                    listOfSeriesToGroup.Add(x);
                }
                x++;
            }
            while (x < chartObjects.Count);

            chartTitle = currentChartSetName;

            AddDebugTimingInfo(BuildNewChart(listOfSeriesToGroup, chartTitle, true), " -- BuildNewChart " + chartTitle);
            //BuildNewChart(listOfSeriesToGroup, chartTitle, true);

            return (TimeSpan)(DateTime.Now - dt);
        }
        
        private void GetChartObjectsFromXmlFile()
        {
            DateTime dt = DateTime.Now;

            Dictionary<string, string> aliases = new Dictionary<string, string>();

            _chartObjectsTable = new DataTable();
            //ChartName, ChartType, LoadTestRunId, InstanceId, Category, Counter, Instance, Machine, SeriesName, fixedScale
            _chartObjectsTable.Columns.Add("ChartName");
            _chartObjectsTable.Columns.Add("ChartType");
            _chartObjectsTable.Columns.Add("LoadTestRunId", System.Type.GetType("System.Int32"));
            _chartObjectsTable.Columns.Add("InstanceId", System.Type.GetType("System.Int32"));
            _chartObjectsTable.Columns.Add("Category");
            _chartObjectsTable.Columns.Add("Counter");
            _chartObjectsTable.Columns.Add("Instance");
            _chartObjectsTable.Columns.Add("Machine");
            _chartObjectsTable.Columns.Add("SeriesName");
            _chartObjectsTable.Columns.Add("fixedScale");
            _chartObjectsTable.Columns.Add("topQty", System.Type.GetType("System.Int32"));
            _chartObjectsTable.Columns.Add("sortOrder");

            string testRigMachineNames = BuildListOfTestRigMachines(currentlyHighlightedRunId);

            try
            {
                XmlDocument xDoc = new XmlDocument();
                xDoc.Load(Properties.Settings.Default.ChartObjectFileName);

                #region -- Variable Names -------------------------------------------------------------
                foreach (XmlNode xNode in xDoc.GetElementsByTagName(Properties.Settings.Default.Charting_MachineAliasSetToUse))
                {
                    foreach (XmlNode xChild in xNode.ChildNodes)
                    {
                        if(xChild.Attributes != null)
                            aliases.Add(xChild.Attributes["name"].Value, xChild.Attributes["machineList"].Value);
                    }
                }
                #endregion

                #region -- Key Metrics -----------------------------------------------------------------------
                foreach (XmlNode xNode in xDoc.GetElementsByTagName("KeyMetricsCounters"))
                {
                    foreach (XmlNode xChild in xNode.ChildNodes)
                    {
                        if (xChild.Attributes != null &&
                            xChild.Attributes["enabled"].Value.ToLower() == "true")
                        {
                            AddKeyMetricChartObjectsToTable(
                                xChild.Attributes["name"].Value,
                                xChild.Attributes["category"].Value,
                                xChild.Attributes["counter"].Value,
                                xChild.Attributes["instance"].Value,
                                xChild.Attributes["fixedscale"].Value,
                                ChartCategory.CoreMetric);
                        }
                    }
                }
                #endregion

                #region -- SUT Counters ----------------------------------------------------------------------
                foreach (XmlNode xNode in xDoc.GetElementsByTagName("SutCounters"))
                {
                    foreach (XmlNode xChild in xNode.ChildNodes)
                    {
                        if (xChild.Attributes["enabled"].Value.ToLower() == "true")
                        {
                            string alias;
                            if (xChild.Attributes["machineList"].Value.Contains("__"))
                                alias = aliases[xChild.Attributes["machineList"].Value];
                            else
                                alias = xChild.Attributes["machineList"].Value;

                            AddMachineChartObjectsToTable(
                                xChild.Attributes["name"].Value,
                                xChild.Attributes["category"].Value,
                                xChild.Attributes["counter"].Value,
                                xChild.Attributes["instance"].Value,
                                alias, 
                                xChild.Attributes["fixedscale"].Value,
                                ChartCategory.SutMachine);
                        }
                    }
                }
                #endregion

                #region -- Test Rig Counters -----------------------------------------------------------------
                foreach (XmlNode xNode in xDoc.GetElementsByTagName("TestRigCounters"))
                {
                    foreach (XmlNode xChild in xNode.ChildNodes)
                    {
                        if (xChild.Attributes["enabled"].Value.ToLower() == "true")
                        {
                            string alias;
                            if (xChild.Attributes["machineList"].Value.Contains("__"))
                                alias = aliases[xChild.Attributes["machineList"].Value];
                            else
                                alias = xChild.Attributes["machineList"].Value;

                            AddMachineChartObjectsToTable(
                                xChild.Attributes["name"].Value,
                                xChild.Attributes["category"].Value,
                                xChild.Attributes["counter"].Value,
                                xChild.Attributes["instance"].Value,
                                alias,
                                xChild.Attributes["fixedscale"].Value,
                                ChartCategory.RigMachine);
                        }
                    }
                }
                #endregion

                #region -- Combo Charts ----------------------------------------------------------------------
                foreach (XmlNode xNode in xDoc.GetElementsByTagName("ComboChartSets"))
                {
                    foreach (XmlNode xChild in xNode.ChildNodes)
                    {
                        if (xChild.Attributes["enabled"].Value.ToLower() == "true")
                        {
                            string chartName = xChild.Attributes["name"].Value;
                            foreach (XmlNode xChild2 in xChild.ChildNodes)
                            {
                                if (xChild2.Attributes["enabled"].Value.ToLower() == "true")
                                {
                                    AddComboChartObjectsToTable(
                                        chartName,
                                        xChild2.Attributes["category"].Value,
                                        xChild2.Attributes["counter"].Value,
                                        xChild2.Attributes["instance"].Value,
                                        xChild2.Attributes["fixedscale"].Value,
                                        ChartCategory.ComboMetric);
                                }
                            }
                        }
                    }
                }
                #endregion

                //#region -- Scorecard Charts ----------------------------------------------------------------------
                //foreach (XmlNode xNode in xDoc.GetElementsByTagName("ScoreCardChartSets"))
                //{
                //    foreach (XmlNode xChild in xNode.ChildNodes)
                //    {
                //        if (xChild.Attributes["enabled"].Value.ToLower() == "true")
                //        {
                //            string chartName = xChild.Attributes["name"].Value;
                //            foreach (XmlNode xChild2 in xChild.ChildNodes)
                //            {
                //                if (xChild2.Attributes["enabled"].Value.ToLower() == "true")
                //                {
                //                    AddScorecardChartObjectsToTable(
                //                        chartName,
                //                        xChild2.Attributes["type"].Value,
                //                        xChild2.Attributes["machine"].Value,
                //                        xChild2.Attributes["category"].Value,
                //                        xChild2.Attributes["counter"].Value,
                //                        xChild2.Attributes["instance"].Value,
                //                        xChild2.Attributes["fixedscale"].Value,
                //                        ChartCategory.ComboMetric);
                //                }
                //            }
                //        }
                //    }
                //}
                //#endregion

                dgRunResults.ItemsSource = _chartObjectsTable.AsDataView();
                AddDebugTimingInfo((TimeSpan)(DateTime.Now - dt), "-----     NewGetChartObjectsFromXmlFile");
            }
            catch (XmlException ex)
            {
                MessageBox.Show(ex.ToString());
            }
        }
        
        private void AddMachineChartObjectsToTable(string displayName, string categoryName, string counterName, string instanceName, string rigMachineNames, string scale, ChartCategory chartCategory)
        {
            string Sql_GETRIGMACHINECHARTOBJECT_INSTANCEIDS = @"SELECT '{0} - Rig' AS [ChartName], 'Rig' AS [ChartType], LoadTestRunId ,InstanceId
	            ,CategoryName AS [Category]	,CounterName AS [Counter] ,Instance, MachineName AS [Machine]
	            ,InstanceName AS [SeriesName], '{1}' AS [fixedScale] FROM MTSL_View_PerfmonInstanceNamesAndIds
                WHERE LoadTestRunId = {2} AND CategoryName = '{3}' AND CounterName = '{4}' AND Instance LIKE '{5}' AND MachineName IN ({6})";

            string Sql_GETSUTMACHINECHARTOBJECT_INSTANCEIDS = @"SELECT '{0} - SUT' AS [ChartName], 'Sut' AS [ChartType], LoadTestRunId ,InstanceId
                    ,CategoryName AS [Category]	,CounterName AS [Counter] ,Instance, MachineName AS [Machine]
                    ,InstanceName AS [SeriesName], '{1}' AS [fixedScale] FROM MTSL_View_PerfmonInstanceNamesAndIds
                WHERE LoadTestRunId = {2} AND CategoryName = '{3}' AND CounterName = '{4}' AND Instance LIKE '{5}' AND MachineName IN ({6})
                ORDER BY [SeriesName] ASC";

            //ChartName, ChartType, LoadTestRunId, InstanceId, Category, Counter, Instance, Machine, SeriesName, fixedScale
            string sqlCmd;
            if (chartCategory == ChartCategory.RigMachine)
            {
                sqlCmd = String.Format(Sql_GETRIGMACHINECHARTOBJECT_INSTANCEIDS, displayName, scale, currentlyHighlightedRunId
                    , categoryName, counterName, instanceName, rigMachineNames);
            }
            else
            {
                sqlCmd = String.Format(Sql_GETSUTMACHINECHARTOBJECT_INSTANCEIDS, displayName, scale, currentlyHighlightedRunId
                    , categoryName, counterName, instanceName, rigMachineNames);
            }

            string sqlConn = Properties.Settings.Default.sqlConn;
            DataTable _table;
            TimeSpan ts = tts.ExecuteReaderTable(sqlCmd, out _table);
            //AddDebugTimingInfo(ts, sqlCmd);
            mostRecentSqlQuery = sqlCmd;

            _chartObjectsTable.Merge(_table);
        }

        private void AddKeyMetricChartObjectsToTable(string displayName, string categoryName, string counterName, string instanceName, string scale, ChartCategory chartCategory)
        {
            //ChartName, ChartType, LoadTestRunId, InstanceId, Category, Counter, Instance, Machine, SeriesName, fixedScale
            string sqlCmd = String.Format(TSL_ReportingEngine.Sql_GETKEYMETRICCHARTOBJECT_INSTANCEIDS, displayName, scale
                , currentlyHighlightedRunId, categoryName, counterName, instanceName);

            string sqlConn = Properties.Settings.Default.sqlConn;
            DataTable _table;
            TimeSpan ts = tts.ExecuteReaderTable(sqlCmd, out _table);
            //AddDebugTimingInfo(ts, sqlCmd);
            mostRecentSqlQuery = sqlCmd;

            _chartObjectsTable.Merge(_table);
        }

        private void AddComboChartObjectsToTable(string displayName, string categoryName, string counterName, string instanceName, string scale, ChartCategory chartCategory)
        {
            //ChartName, ChartType, LoadTestRunId, InstanceId, Category, Counter, Instance, Machine, SeriesName, fixedScale
            string sqlCmd = String.Format(TSL_ReportingEngine.Sql_GETCOMBOMACHINECHARTOBJECT_INSTANCEIDS, displayName, scale
                , currentlyHighlightedRunId, categoryName, counterName, instanceName);

            string sqlConn = Properties.Settings.Default.sqlConn;
            DataTable _table;
            TimeSpan ts = tts.ExecuteReaderTable(sqlCmd, out _table);
            //AddDebugTimingInfo(ts, sqlCmd);
            mostRecentSqlQuery = sqlCmd;

            _chartObjectsTable.Merge(_table);
        }

        private TimeSpan AddChartObjectsToSeriesSets()
        {
            DateTime dt = DateTime.Now;
            //0 ChartName, 
            //1 ChartType, 
            //2 LoadTestRunId, 
            //3 InstanceId, 
            //4 Category, 
            //5 Counter, 
            //6 Instance, 
            //7 Machine, 
            //8 SeriesName, 
            //9 fixedScale
            ClearChartObjectList();

            string currentChartName = "";

            for (int x = 0; x < _chartObjectsTable.Rows.Count; x++)
            {
                TSL_ChartingDataSeries series = new TSL_ChartingDataSeries();
                series.name = _chartObjectsTable.Rows[x][0].ToString();
                
                //Reset the plot colors as needed (all series with the same name go on the same chart
                // and the datatable is already sorted by series name.
                if (currentChartName != series.name)
                {
                    currentChartName = series.name;
                    currentPlotColor = 0;
                }

                series.runId = (int)_chartObjectsTable.Rows[x][2];
                series.instanceId = (int)_chartObjectsTable.Rows[x][3];
                series.fullCounterName = _chartObjectsTable.Rows[x][8].ToString();
                series.name = _chartObjectsTable.Rows[x][0].ToString();
                series.machineName = _chartObjectsTable.Rows[x][7].ToString();
                series.categoryName = _chartObjectsTable.Rows[x][4].ToString();
                series.counterName = _chartObjectsTable.Rows[x][5].ToString();
                series.instanceName = _chartObjectsTable.Rows[x][6].ToString();
                series.lineColor = plotColors[currentPlotColor % plotColors.Count];
                series.chartObjectScale = (_chartObjectsTable.Rows[x][9].ToString().ToLower() == "false") ? 0 : 100;                
                series.intervalSpan = re.listOfRuns[(int)_chartObjectsTable.Rows[x][2]].sampleRate;
                currentPlotColor++;
                series.timeToRetrieveIntervalValues = GetIntervalValues(ref series);
                chartObjects.Add(series.ShallowCopy());
            }
            return (TimeSpan)(DateTime.Now - dt);
        }

        private string BuildListOfTestRigMachines(int runId)
        {
            // This method gets all of the agents and the controller and builds a string in the proper format
            // for passing into a SQL query that uses the 'IN' clause

            StringBuilder sb = new StringBuilder();
            if (!re.listOfRuns[runId].resultTypes.HasFlag(ResultTypesStoredInLoadTest.IsLocalRun))
            {
                foreach (string str in re.listOfRuns[runId].agents)
                {
                    sb.Append(String.Format("'{0}',", str));
                }
            }

            sb.Append(String.Format("'{0}'", re.listOfRuns[runId].controllerName));
            
            return sb.ToString();
        }
        #endregion

        #region -- Interval Table Reporting Methods -------------------------------------------------------------------------------------
        string MSG_NoIntervalsDetected = "No intervals meeting the 'Constant Load' criteria were detected in this run. The Criteria is that the 'User Load' counter have a minimum of {0} contiguous samples over a minimum timeframe of {1} seconds that contain the same value. Please refer to the 07_Ribbon_Tables Help Topic for more information.";
        private void AddTestIntervalTablesToReport()
        {
            if (!Properties.Settings.Default.includeTestTimesForIntervalReporting)
                return;
            if (!re.listOfRuns[currentlyHighlightedRunId].resultTypes.HasFlag(ResultTypesStoredInLoadTest.ContainsStepLoad))
                return;

            List<int[]> intervals = new List<int[]>();
            re.GetConstantLoadIntervals(out intervals, currentlyHighlightedRunId);
            if (intervals.Count == 0)
            {
                string errorMessage = String.Format(MSG_NoIntervalsDetected,
                    Properties.Settings.Default.minimumPointsForConstantInterval, Properties.Settings.Default.minimumSecondsForConstantInterval);
                rtbResults.Document.Blocks.Add(ReportGenerator.TSL_ReportGenerator.AddCommentLineToReport(errorMessage, Colors.Red));
                rtbResults.UpdateLayout();
                return;
            }

            rtbResults.Document.Blocks.Add(ReportGenerator.TSL_ReportGenerator.AddSectionTitleToReport("Test Timings Split Into Constant Intervals"));
            string sqlQuery = @"EXEC VSLT_prc_Interval_TestResults @RunId = {0}, @NameFilter = {1}, @StartOffset = {2}, @EndOffset = {3}, @NumberOfRows = {4}";

            foreach (int[] interval in intervals)
            {
                string sqlCmd = String.Format(sqlQuery, currentlyHighlightedRunId, tbTableFilterString.Text,
                    interval[0], interval[1], Properties.Settings.Default.numTopSlowest);
                DataTable _table;
                TalkToSql tts = new TalkToSql(re.sqlConn, Properties.Settings.Default.ConnectionTimeout, Properties.Settings.Default.CommandTimeout);
                tts.ExecuteReaderTable(sqlCmd, out _table);
                mostRecentSqlQuery = sqlCmd;
                string tableTitle = String.Format("Test Timing Info for {0} Users: {1}-{2} Second Time Range"
                    , interval[2], interval[0], interval[1]);

                Typeface font = new Typeface("Arial");
                FlowDocument flowDocTemp = ReportGenerator.TSL_ReportGenerator.CreateXamlTableFromDataTable(tableTitle, _table, new List<int>(new int[] { 20, -1 }), font, 10, 40);

                rtbResults.Document.Blocks.Add(flowDocTemp.Blocks.FirstBlock);
            }
            rtbResults.Document.Blocks.Add(ReportGenerator.TSL_ReportGenerator.AddSeparatorLineToReport().Blocks.FirstBlock);
            rtbResults.UpdateLayout();
        }

        private void AddTransactionIntervalTablesToReport()
        {
            if (!re.listOfRuns[currentlyHighlightedRunId].resultTypes.HasFlag(ResultTypesStoredInLoadTest.ContainsStepLoad) ||
                !re.listOfRuns[currentlyHighlightedRunId].resultTypes.HasFlag(ResultTypesStoredInLoadTest.ContainsTransactions))
                return;

            List<int[]> intervals = new List<int[]>();
            re.GetConstantLoadIntervals(out intervals, currentlyHighlightedRunId);
            if (intervals.Count == 0)
            {
                string errorMessage = String.Format(MSG_NoIntervalsDetected,
                    Properties.Settings.Default.minimumPointsForConstantInterval, Properties.Settings.Default.minimumSecondsForConstantInterval);
                rtbResults.Document.Blocks.Add(ReportGenerator.TSL_ReportGenerator.AddCommentLineToReport(errorMessage, Colors.Red));
                rtbResults.UpdateLayout();
                return;
            }

            rtbResults.Document.Blocks.Add(ReportGenerator.TSL_ReportGenerator.AddSectionTitleToReport("Transaction Timings Split Into Constant Intervals"));
            string sqlQuery = @"EXEC VSLT_prc_Interval_TransactionResults @RunId = {0}, @NameFilter = {1}, @StartOffset = {2}, @EndOffset = {3}, @NumberOfRows = {4}";

            foreach (int[] interval in intervals)
            {
                string sqlCmd = String.Format(sqlQuery, currentlyHighlightedRunId, tbTableFilterString.Text,
                    interval[0], interval[1], Properties.Settings.Default.numTopSlowest);
                DataTable _table;
                TalkToSql tts = new TalkToSql(re.sqlConn, Properties.Settings.Default.ConnectionTimeout, Properties.Settings.Default.CommandTimeout);
                tts.ExecuteReaderTable(sqlCmd, out _table);
                mostRecentSqlQuery = sqlCmd;
                string tableTitle = String.Format("Transaction Timing Info for {0} Users: {1}-{2} Second Time Range"
                    , interval[2], interval[0], interval[1]);

                Typeface font = new Typeface("Arial");
                FlowDocument flowDocTemp = ReportGenerator.TSL_ReportGenerator.CreateXamlTableFromDataTable(tableTitle, _table, new List<int>(new int[] { 20, -1 }), font, 10, 32);

                rtbResults.Document.Blocks.Add(flowDocTemp.Blocks.FirstBlock);
            }
            rtbResults.Document.Blocks.Add(ReportGenerator.TSL_ReportGenerator.AddSeparatorLineToReport().Blocks.FirstBlock);
            rtbResults.UpdateLayout();
        }

        private void AddPageIntervalTablesToReport()
        {
            if (!re.listOfRuns[currentlyHighlightedRunId].resultTypes.HasFlag(ResultTypesStoredInLoadTest.ContainsStepLoad) || 
                !re.listOfRuns[currentlyHighlightedRunId].resultTypes.HasFlag(ResultTypesStoredInLoadTest.ContainsPages))
                return;

            List<int[]> intervals = new List<int[]>();
            re.GetConstantLoadIntervals(out intervals, currentlyHighlightedRunId);
            if (intervals.Count == 0)
            {
                string errorMessage = String.Format(MSG_NoIntervalsDetected,
                    Properties.Settings.Default.minimumPointsForConstantInterval, Properties.Settings.Default.minimumSecondsForConstantInterval);
                rtbResults.Document.Blocks.Add(ReportGenerator.TSL_ReportGenerator.AddCommentLineToReport(errorMessage, Colors.Red));
                rtbResults.UpdateLayout();
                return;
            }

            rtbResults.Document.Blocks.Add(ReportGenerator.TSL_ReportGenerator.AddSectionTitleToReport("Page Timings Split Into Constant Intervals"));
            string sqlQuery = @"EXEC VSLT_prc_Interval_PageResults @RunId = {0}, @NameFilter = {1}, @StartOffset = {2}, @EndOffset = {3}, @NumberOfRows = {4}";

            ReportGenerator.CellHighlighting highLighting = new ReportGenerator.CellHighlighting();

            foreach (int[] interval in intervals)
            {
                string sqlCmd = String.Format(sqlQuery, currentlyHighlightedRunId, tbTableFilterString.Text,
                    interval[0], interval[1], Properties.Settings.Default.numTopSlowest);
                DataTable _table;
                TalkToSql tts = new TalkToSql(re.sqlConn, Properties.Settings.Default.ConnectionTimeout, Properties.Settings.Default.CommandTimeout);
                tts.ExecuteReaderTable(sqlCmd, out _table);
                mostRecentSqlQuery = sqlCmd;
                string tableTitle = String.Format("Page Timing Info for {0} Users: {1}-{2} Second Time Range"
                    , interval[2], interval[0], interval[1]);

                Typeface font = new Typeface("Arial");
                FlowDocument flowDocTemp = ReportGenerator.TSL_ReportGenerator.CreateXamlTableFromDataTable(tableTitle, _table, new List<int>(new int[] { 20, -1 }), font, 10, highLighting, 32);
                rtbResults.Document.Blocks.Add(flowDocTemp.Blocks.FirstBlock);
                rtbResults.Document.Blocks.Add(ReportGenerator.TSL_ReportGenerator.AddCommentLineToReport("   \r\n", Colors.Beige, 6));
            }
            rtbResults.Document.Blocks.Add(ReportGenerator.TSL_ReportGenerator.AddSeparatorLineToReport().Blocks.FirstBlock);
            rtbResults.UpdateLayout();
        }

        private void AddPerfmonIntervalTablesToReport(string machineFilter, string category, string counter, string instance, string sectionTitle)
        {
            if (!re.listOfRuns[currentlyHighlightedRunId].resultTypes.HasFlag(ResultTypesStoredInLoadTest.ContainsStepLoad))
                return;

            List<int[]> intervals = new List<int[]>();
            re.GetConstantLoadIntervals(out intervals, currentlyHighlightedRunId);
            if(intervals.Count == 0)
            {
                string errorMessage = String.Format(MSG_NoIntervalsDetected,
                    Properties.Settings.Default.minimumPointsForConstantInterval, Properties.Settings.Default.minimumSecondsForConstantInterval);
                rtbResults.Document.Blocks.Add(ReportGenerator.TSL_ReportGenerator.AddCommentLineToReport(errorMessage, Colors.Red));
                rtbResults.UpdateLayout();
                return;
            }

            rtbResults.Document.Blocks.Add(ReportGenerator.TSL_ReportGenerator.AddSectionTitleToReport(sectionTitle));
            string sqlQuery;
            if (machineFilter == "")
            {
                sqlQuery = @"EXEC VSLT_prc_Interval_PerfmonCounters @RunId = {0}
                    , @Category = '{1}', @Counter = '{2}', @Instance = '{3}'
                    , @StartOffset = {4}, @EndOffset = {5}";
            }
            else
            {
                sqlQuery = @"EXEC VSLT_prc_Interval_PerfmonCounters @RunId = {0}
                    , @Category = '{1}', @Counter = '{2}', @Instance = '{3}'
                    , @StartOffset = {4}, @EndOffset = {5}, @Machine = '{6}'";
            }

            foreach (int[] interval in intervals)
            {
                string sqlCmd = String.Format(sqlQuery, currentlyHighlightedRunId
                    ,category, counter, instance, interval[0], interval[1], machineFilter);
                DataTable _table;
                TalkToSql tts = new TalkToSql(re.sqlConn, Properties.Settings.Default.ConnectionTimeout, Properties.Settings.Default.CommandTimeout);
                tts.ExecuteReaderTable(sqlCmd, out _table);
                mostRecentSqlQuery = sqlCmd;
                string tableTitle = String.Format("{0} Users: {1}-{2} Second Time Range"
                    , interval[2], interval[0], interval[1]);

                Typeface font = new Typeface("Arial");
                FlowDocument flowDocTemp = ReportGenerator.TSL_ReportGenerator.CreateXamlTableFromDataTable(tableTitle, _table, new List<int>(new int[] { 20, -1 }), font, 10, 32);

                rtbResults.Document.Blocks.Add(flowDocTemp.Blocks.FirstBlock);
            }
            rtbResults.Document.Blocks.Add(ReportGenerator.TSL_ReportGenerator.AddSeparatorLineToReport().Blocks.FirstBlock);
            rtbResults.UpdateLayout();
        }
        #endregion
    }

    public class DocumentPaginatorWrapper : DocumentPaginator
    {
        Size m_PageSize;
        Size m_Margin;
        DocumentPaginator m_Paginator;
        Typeface m_Typeface;

        public DocumentPaginatorWrapper(DocumentPaginator paginator, Size pageSize, Size margin)
        {
            m_PageSize = pageSize;
            m_Margin = margin;
            m_Paginator = paginator;

            m_Paginator.PageSize = new Size(m_PageSize.Width - margin.Width * 2,
                                            m_PageSize.Height - margin.Height * 2);
        }

        Rect Move(Rect rect)
        {
            if (rect.IsEmpty)
            {
                return rect;
            }
            else
            {
                return new Rect(rect.Left + m_Margin.Width, rect.Top + m_Margin.Height,
                                rect.Width, rect.Height);
            }
        }

        public override DocumentPage GetPage(int pageNumber)
        {
            DocumentPage page = m_Paginator.GetPage(pageNumber);

            // Create a wrapper visual for transformation and add extras
            ContainerVisual newpage = new ContainerVisual();

            DrawingVisual title = new DrawingVisual();

            using (DrawingContext ctx = title.RenderOpen())
            {
                if (m_Typeface == null)
                {
                    m_Typeface = new Typeface("Times New Roman");
                }

                FormattedText text = new FormattedText("Page " + (pageNumber + 1),
                    System.Globalization.CultureInfo.CurrentCulture, FlowDirection.LeftToRight,
                    m_Typeface, 14, Brushes.Black);

                ctx.DrawText(text, new Point(0, -96 / 4)); // 1/4 inch above page content
            }

            DrawingVisual background = new DrawingVisual();

            using (DrawingContext ctx = background.RenderOpen())
            {
                ctx.DrawRectangle(new SolidColorBrush(Color.FromRgb(240, 240, 240)), null, page.ContentBox);
            }

            newpage.Children.Add(background); // Scale down page and center

            ContainerVisual smallerPage = new ContainerVisual();
            smallerPage.Children.Add(page.Visual);
            smallerPage.Transform = new MatrixTransform(0.95, 0, 0, 0.95,
                0.025 * page.ContentBox.Width, 0.025 * page.ContentBox.Height);

            newpage.Children.Add(smallerPage);
            newpage.Children.Add(title);

            newpage.Transform = new TranslateTransform(m_Margin.Width, m_Margin.Height);

            return new DocumentPage(newpage, m_PageSize, Move(page.BleedBox), Move(page.ContentBox));
        }

        public override bool IsPageCountValid
        {
            get
            {
                return m_Paginator.IsPageCountValid;
            }
        }

        public override int PageCount
        {
            get
            {
                return m_Paginator.PageCount;
            }
        }

        public override Size PageSize
        {
            get
            {
                return m_Paginator.PageSize;
            }

            set
            {
                m_Paginator.PageSize = value;
            }
        }

        public override IDocumentPaginatorSource Source
        {
            get
            {
                return m_Paginator.Source;
            }
        }
    }
}

